package net.jgrm.multilevelcache.service;

import net.jgrm.multilevelcache.sync.CacheSyncPublisher;
import net.jgrm.redisadvanced.lock.RedisLockService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 线程安全的缓存服务
 * 解决高并发环境下的缓存一致性问题
 * <p>
 * 采用的缓存安全策略：
 * 1. 分布式锁防止缓存击穿（使用Redisson实现）
 * 2. Double-Check机制防止重复查询
 * 3. 延迟双删策略确保缓存一致性
 * 4. 空值缓存防止穿透攻击
 * 5. 多级缓存自动同步（Caffeine + Redis）
 *
 * @author jgrm
 */
@Service
public class ThreadSafeCacheService {

    private static final Logger logger = LoggerFactory.getLogger(ThreadSafeCacheService.class);

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private RedisLockService redisLockService;

    @Autowired(required = false)
    private CacheSyncPublisher cacheSyncPublisher;

    // 分布式锁前缀
    private static final String LOCK_PREFIX = "cache:lock:";
    // 缓存空值标识
    private static final String NULL_VALUE = "NULL_VALUE";
    // 锁超时时间（秒）
    private static final long LOCK_TIMEOUT = 30;
    // 锁等待时间（秒）
    private static final long LOCK_WAIT_TIME = 5;
    // 空值缓存时间（秒）
    private static final long NULL_CACHE_TIMEOUT = 60;

    /**
     * 线程安全的缓存读取
     * 使用分布式锁防止缓存击穿
     *
     * @param cacheName  缓存名称
     * @param key        缓存键
     * @param type       返回类型
     * @param dataLoader 数据加载器（缓存未命中时调用）
     * @return 缓存数据或通过dataLoader加载的数据
     */
    public <T> T getFromCache(String cacheName, Object key, Class<T> type, Supplier<T> dataLoader) {
        Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            logger.warn("Cache '{}' not found", cacheName);
            return dataLoader.get();
        }

        String cacheKey = String.valueOf(key);

        // 1. 尝试从缓存获取
        Cache.ValueWrapper valueWrapper = cache.get(key);
        if (valueWrapper != null) {
            Object value = valueWrapper.get();
            if (NULL_VALUE.equals(value)) {
                return null; // 缓存的空值
            }
            return type.cast(value);
        }

        // 2. 缓存未命中，使用分布式锁防止缓存击穿
        String lockKey = LOCK_PREFIX + cacheName + ":" + cacheKey;

        try {
            // 使用 RedisLockService 获取分布式锁，设置等待时间和锁超时时间
            if (redisLockService.tryLock(lockKey, LOCK_WAIT_TIME, LOCK_TIMEOUT, TimeUnit.SECONDS)) {
                try {
                    // 获得锁后再次检查缓存（Double-Check）
                    valueWrapper = cache.get(key);
                    if (valueWrapper != null) {
                        Object value = valueWrapper.get();
                        if (NULL_VALUE.equals(value)) {
                            return null;
                        }
                        return type.cast(value);
                    }

                    // 从数据源加载数据
                    T data = dataLoader.get();

                    // 写入缓存（包括空值）
                    if (data != null) {
                        cache.put(key, data);
                        logger.debug("Cached data for key: {}", cacheKey);
                    } else {
                        // 缓存空值，防止缓存穿透
                        cache.put(key, NULL_VALUE);
                        logger.debug("Cached null value for key: {}", cacheKey);
                    }

                    return data;

                } finally {
                    // 释放锁
                    redisLockService.unlock(lockKey);
                }
            } else {
                // 未获得锁，等待一小段时间后重试
                try {
                    Thread.sleep(50); // 50ms
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                // 重试一次缓存读取
                valueWrapper = cache.get(key);
                if (valueWrapper != null) {
                    Object value = valueWrapper.get();
                    if (NULL_VALUE.equals(value)) {
                        return null;
                    }
                    return type.cast(value);
                }

                // 如果仍然未命中，直接查询数据库（降级策略）
                logger.warn("Cache lock timeout, fallback to direct database query for key: {}", cacheKey);
                return dataLoader.get();
            }

        } catch (InterruptedException e) {
            logger.error("Interrupted while waiting for cache lock for key: {}", cacheKey, e);
            Thread.currentThread().interrupt();
            // 中断时直接查询数据库
            return dataLoader.get();
        } catch (Exception e) {
            logger.error("Error in thread-safe cache operation for key: {}", cacheKey, e);
            // 出错时直接查询数据库
            return dataLoader.get();
        }
    }

    /**
     * 线程安全的缓存更新
     * 使用写锁确保更新的原子性
     *
     * @param cacheName 缓存名称
     * @param key       缓存键
     * @param data      要缓存的数据，null时清除缓存
     */
    public <T> void updateCache(String cacheName, Object key, T data) {
        Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            logger.warn("Cache '{}' not found", cacheName);
            return;
        }

        String cacheKey = String.valueOf(key);
        String lockKey = LOCK_PREFIX + cacheName + ":" + cacheKey + ":write";

        try {
            // 使用写锁确保更新的原子性
            if (redisLockService.tryLock(lockKey, LOCK_WAIT_TIME, LOCK_TIMEOUT, TimeUnit.SECONDS)) {
                try {
                    if (data != null) {
                        cache.put(key, data);
                        logger.debug("Updated cache for key: {}", cacheKey);
                    } else {
                        cache.evict(key);
                        logger.debug("Evicted cache for key: {}", cacheKey);
                    }
                } finally {
                    redisLockService.unlock(lockKey);
                }
            } else {
                logger.warn("Failed to acquire write lock for cache key: {}", cacheKey);
            }
        } catch (InterruptedException e) {
            logger.error("Interrupted while waiting for write lock for key: {}", cacheKey, e);
            Thread.currentThread().interrupt();
        } catch (Exception e) {
            logger.error("Error updating cache for key: {}", cacheKey, e);
        }
    }

    /**
     * 线程安全的缓存删除
     * 支持延迟双删策略
     *
     * @param cacheName 缓存名称
     * @param key       缓存键
     */
    public void evictCache(String cacheName, Object key) {
        evictCache(cacheName, key, false);
    }

    /**
     * 线程安全的缓存删除
     * 支持延迟双删策略和分布式缓存同步
     *
     * @param cacheName           缓存名称
     * @param key                 缓存键
     * @param delayedDoubleDelete 是否启用延迟双删策略
     */
    public void evictCache(String cacheName, Object key, boolean delayedDoubleDelete) {
        Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            logger.warn("Cache '{}' not found", cacheName);
            return;
        }

        String cacheKey = String.valueOf(key);

        try {
            // 第一次删除本地缓存
            cache.evict(key);
            logger.debug("First eviction for cache key: {}", cacheKey);

            // 发布缓存同步消息（通知其他实例删除本地缓存）
            if (cacheSyncPublisher != null && isCaffeineCache(cache)) {
                cacheSyncPublisher.publishEvict(cacheName, key);
                logger.debug("Published cache evict message for key: {}", cacheKey);
            }

            if (delayedDoubleDelete) {
                // 延迟双删：在异步线程中执行第二次删除
                new Thread(() -> {
                    try {
                        Thread.sleep(500); // 延迟500ms
                        cache.evict(key);
                        logger.debug("Second eviction for cache key: {}", cacheKey);

                        // 再次发布同步消息确保一致性
                        if (cacheSyncPublisher != null && isCaffeineCache(cache)) {
                            cacheSyncPublisher.publishEvict(cacheName, key);
                            logger.debug("Published delayed cache evict message for key: {}", cacheKey);
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        logger.warn("Delayed double delete interrupted for key: {}", cacheKey);
                    }
                }, "delayed-cache-delete-" + cacheKey).start();
            }

        } catch (Exception e) {
            logger.error("Error evicting cache for key: {}", cacheKey, e);
        }
    }

    /**
     * 预热缓存
     *
     * @param cacheName 缓存名称
     * @param key       缓存键
     * @param data      要预热的数据
     */
    public <T> void warmupCache(String cacheName, Object key, T data) {
        if (data != null) {
            updateCache(cacheName, key, data);
            logger.debug("Warmed up cache for key: {}", key);
        }
    }

    /**
     * 批量删除缓存
     * 支持分布式缓存同步
     *
     * @param cacheName           缓存名称
     * @param keys                缓存键集合
     * @param delayedDoubleDelete 是否启用延迟双删策略
     */
    public void evictCacheBatch(String cacheName, Iterable<?> keys, boolean delayedDoubleDelete) {
        for (Object key : keys) {
            evictCache(cacheName, key, delayedDoubleDelete);
        }
    }

    /**
     * 清空指定缓存的所有数据
     * 支持分布式缓存同步
     *
     * @param cacheName 缓存名称
     */
    public void clearCache(String cacheName) {
        Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            logger.warn("Cache '{}' not found", cacheName);
            return;
        }

        try {
            cache.clear();
            logger.debug("Cleared all entries in cache: {}", cacheName);

            // 发布缓存清空同步消息（通知其他实例清空本地缓存）
            if (cacheSyncPublisher != null && isCaffeineCache(cache)) {
                cacheSyncPublisher.publishClear(cacheName);
                logger.debug("Published cache clear message for cache: {}", cacheName);
            }
        } catch (Exception e) {
            logger.error("Error clearing cache: {}", cacheName, e);
        }
    }

    /**
     * 检查是否为Caffeine缓存（需要同步）
     */
    private boolean isCaffeineCache(Cache cache) {
        return cache instanceof CaffeineCache;
    }
}